import EventCenter from "../lib/EventCenter";
import GameCfgData from "../lib/GameCfgData";
import MathTool from "../lib/MathTool";
import SoundManager from "../lib/SoundManger";
import AnimManager from "./AnimManager";
import EventEunm from "./data/EventEunm";
import GateData from "./data/GateData";
import ItemBaseData from "./data/ItemBaseData";
import PlayerData from "./data/PlayerData";
import Grid from "./Grid";
import Item from "./view/Item";
import TxtSp from "./view/TxtSp";

export default class GridManager {
    private static _ins: GridManager;
    private mGrid: Grid;
    private mGateData: GateData;
    private mAwardItemIds = [];
    public score: number = 0;
    public diamand: number = 0;
    public maxDiamand: number = 0;
    public maxDiamand2: number = 0;
    public distroyPaoNum: number = 0;
    public maxPao: number = 0;
    public scoreStep = 0;
    public stepScore = 5;
    public isWuJiMode: boolean;
    public currDaojuStep: number = 0;
    /**第一个道具cd*/
    public maxDaojuStep: number = 7;
    public currDaojuStep2: number = 0;
    /**无尽模式上面的随机物品出现cd*/
    public maxDaojuStep2: number = 10
    public showRow: number = 12;
    public showRowY: number = 0;
    private mItemParent: Laya.Sprite;
    private mItemparentY: number;
    private mScene: any;
    private mSpRemoveIndex: number = 0;
    private mIsMoving: boolean;
    private mHardLv: number = 0;
    private mCurrItemData: ItemBaseData;
    public lvTotalStep: number = 0;
    private mWuJinRow: number = 100;
    constructor() {
        this.mGrid = new Grid();
        this.showRowY = this.showRow * ItemBaseData.WIDTH;
    }


    public initData(_cfg, _sp: Laya.Sprite, _scene: any, _isWuJi: boolean) {
        this.clear();
        this.restDaojuStep();
        this.restDaojuStep2();
        this.score = 0;
        this.diamand = 0;
        this.distroyPaoNum = 0;
        this.scoreStep = 0;
        this.mHardLv = 0;
        this.mIsMoving = false;
        this.mItemParent = _sp;
        this.mScene = _scene;
        this.isWuJiMode = _isWuJi;
        this.mItemparentY = this.mItemParent.y;
        this.mGrid.setItemParent(_sp);
        if (!this.isWuJiMode) {
            this.currDaojuStep = 0;
            this.currDaojuStep2 = 0;
            this.mGateData = new GateData();
            this.mGateData.parse2(_cfg);
            this.mGrid.setData(this.mGateData.maxRow + 10, this.mGateData.itemvos);
            let task = this.mGrid.getTaskNum();
            this.maxPao = task.num1;
            this.maxDiamand = task.num2;
            this.maxDiamand2 = task.num3;
            this.gateData.initStarScore(this.mGrid.totalDropScore);
        } else {
            this.initWuJinGrid();
        }

    }

    public initWuJinGrid() {
        this.currDaojuStep = 0;
        this.currDaojuStep2 = 0;
        this.mWuJinRow = this.getHardRow();
        this.mGrid.setR(this.mWuJinRow + 10, true);
        let finalNum=0;
        let tmin = 5;
        let tnum =0;
        let tarr=[];
        for (let row = 0; row < this.mWuJinRow; row++) {
            tarr = this.getCanAddPosByRow(row);
            if(row==0)finalNum=tarr.length;
            else{
                tnum = Math.floor(Math.random()*tarr.length);
                finalNum = Math.max(tmin,tnum);
            }
            for (let i = 0; i < finalNum; i++) {
                let trc = tarr.splice(Math.floor(Math.random()*tarr.length),1)[0];
                this.wuJinAdd(trc.row, trc.col);
            }
        }
        this.checkItemParentPos(false, -300);
    }

    private getCanAddPosByRow(_row:number){
        let tarr=[];
        for(let col=0;col<this.mGrid.col;col++){
            if(this.mGrid.canAdd(_row,col))tarr.push({row:_row,col:col});
        }
        return tarr;
    }

    public flyOverCheck(_item: Item) {
        this.mCurrItemData = _item.data.clone();
        this.mSpRemoveIndex = 0;
        let titem: Item;
        let tspdata = this.getConnectSpItem(_item);
        let tarr2 = [];
        if (_item.data.isPao) {
            let tarr: Array<Item> = this.mGrid.findSame(_item);
            if (tarr.length >= 3) tarr2 = tarr;
        } else if (_item.data.isDisposeConnectProp) {
            let tchahe = {};
            let tbrcs = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
            for (let i = 0; i < tbrcs.length; i++) {
                titem = this.grid.getItem(tbrcs[i].row, tbrcs[i].col);
                if (titem && titem.data.colorIndex && !tchahe[titem.data.rcFlag]) {
                    if (!tarr2.length) tarr2 = this.mGrid.findSame(titem);
                    else {
                        tarr2 = tarr2.concat(this.mGrid.findSame(titem));
                    }
                }
            }
            tchahe[_item.data.rcFlag] = 1;
            tarr2.push(_item);
        }
        let tcache2 = {};

        let hasitem: boolean;
        if (tarr2.length) {
            if (!hasitem) {
                this.scoreStep++
                hasitem = true;
            }
            this.removeItems(tarr2, tcache2);
        }
        if (tspdata.spArr.length) {
            if (!hasitem) {
                this.scoreStep++
                hasitem = true;
            }

            tcache2[_item.data.rcFlag] = 1;
            this.removeSpItems(tspdata.spArr, tcache2);
        }
        if (tspdata.arr.length) {
            if (!hasitem) {
                this.scoreStep++
                hasitem = true;
            }
            this.removeItems(tspdata.arr, tcache2);
        }


        if (hasitem) {
            this.addDaojuStep();
        }
        else {
            this.scoreStep = 0;
            if (this.currDaojuStep < this.maxDaojuStep) this.restDaojuStep();
        }
        return { timeIndex: this.mSpRemoveIndex, find: hasitem };
    }

    public addDaojuStep() {
        this.lvTotalStep++;
        this.currDaojuStep++;
        //let tisMax:boolean;
        if (this.currDaojuStep > this.maxDaojuStep) {
            //tisMax=true;
            this.currDaojuStep = this.maxDaojuStep;
        }
        if (this.isWuJiMode) {
            this.currDaojuStep2++;
            if (this.currDaojuStep2 > this.maxDaojuStep2) {
                //tisMax=true;
                this.currDaojuStep2 = this.maxDaojuStep2;
            }
        }
        EventCenter.getIns().event(EventEunm.daojuMax);
    }

    public restDaojuStep() {
        this.currDaojuStep = 0;
        EventCenter.getIns().event(EventEunm.daojuMax);
    }
    public restDaojuStep2() {
        this.currDaojuStep2 = 0;
    }

    public getDaojuPross(_type: number = 1) {
        if (_type == 2) return this.currDaojuStep2 / this.maxDaojuStep2;
        else return this.currDaojuStep / this.maxDaojuStep;
    }

    public removeItems(_arr, _cache = null) {
        let titem: Item;
        for (let i = 0; i < _arr.length; i++) {
            titem = _arr[i];
            if (_cache) {
                if (_cache[titem.data.rcFlag]) continue;
                _cache[titem.data.rcFlag] = 1;
            }
            this.grid.removeItem(titem, false);
            if (titem.data.isDiamandType) this.goDiamandPos(titem);
            else this.removeItem(titem);
        }
    }

    public addScore(_vale: number) {
        this.score += _vale;
    }

    private removeSpItems(_arr, _cache = {}) {
        this.mSpRemoveIndex++;
        let tarr = [];
        let titem: Item;
        let time: number = 0;
        for (let i = 0; i < _arr.length; i++) {
            titem = _arr[i];
            if (_cache[titem.data.rcFlag]) continue;
            if (titem.data.isChangeToTouch) {
                if (this.mCurrItemData.isPao) {
                    this.changeDataToId(titem, this.mCurrItemData.id);
                } else {
                    _cache[titem.data.rcFlag] = 1;
                }
            } else {
                _cache[titem.data.rcFlag] = 1;
                this.grid.removeItem(titem, false);
                time = this.getDelayTime(true);
                Laya.timer.once(time, this, (_item: Item) => {
                    this.removeItem(_item);
                }, [titem]);
                time = this.getDelayTime(true);
                if (titem.data.is3RandomDispose) {
                    tarr = tarr.concat(this.findAndRemoveRandomItems(titem, _cache));
                } else if (titem.data.isBomb) {
                    tarr = tarr.concat(this.findAndRemoveBombItems(titem, _cache));
                    this.addEff(titem, time);
                } else if (titem.data.isRowLine) {
                    tarr = tarr.concat(this.findLineItems(titem, _cache));
                    this.addEff(titem, time);
                }
                else {
                    this.addEff(titem, time);
                }
            }
        }

        if (tarr.length) this.removeSpItems(tarr, _cache);
    }

    public checkDropItems() {
        let tdrops = this.mGrid.findDropItems();
        let titem: Item;
        let time: number = 0;
        if (tdrops.length) {
            let tarr = [];
            for (let i = 0; i < tdrops.length; i++) {
                tarr = tdrops[i]
                for (let j = 0; j < tarr.length; j++) {
                    titem = this.mGrid.removeItem(tarr[j], false);
                    time = this.getDelayTime(false);
                    Laya.timer.once(time, this, (_item: Item) => {
                        if (_item.data.isDiamandType) {
                            this.goDiamandPos(_item);
                        }
                        else {
                            this.dropItem(_item);
                        }
                    }, [titem]);
                }
            }
        }
    }

    private addTxt(_x: number, _y: number, _score: number) {
        let txt: TxtSp = TxtSp.createByPool();
        this.addScore(_score);
        txt.show(this.mGrid.itemParent, _x, _y, "+" + _score);

    }

    private addEff(_item: Item, _delay: number) {
        Laya.timer.once(_delay, this, () => {
            if (_item.data.isLineProp) {
                AnimManager.showRowLineEff(this.mItemParent, _item.x, _item.y);
            } else if (_item.data.isBomb) {
                SoundManager.playSound("bomb.mp3")
                AnimManager.show(this.mItemParent, _item.x - 70, _item.y - 70, AnimManager.BombAnim);
            }

        })
    }

    private addTEff(_x: number, _y: number, _arrs) {
        let time = this.getDelayTime(true);
        Laya.timer.once(time, this, () => {
            AnimManager.showTEff(this.mItemParent, _x, _y, _arrs);
        });
    }

    private dropItem(titem: Item) {
        let ty: number = this.mGrid.itemParent.globalToLocal(new Laya.Point(titem.x, 1000)).y;
        //let tx=30+Math.random()*630;
        let ty2 = titem.y + 300 + Math.random() * 300;
        if (ty2 > ty) ty2 = ty;
        let ttime = (ty2 - titem.y) * 0.9;
        Laya.Tween.to(titem, { y: titem.y + 10 }, 200, null, Laya.Handler.create(this, () => {
            Laya.Tween.to(titem, { y: ty2 }, ttime, null, Laya.Handler.create(this, () => {
                this.removeItem(titem, true, true);
            }))
        }))
    }

    public clearGridData(_item: Item) {
        this.grid.clearGridData(_item);
    }
    /**此处的移除是移除那些从网格中清除了信息但是没有移出舞台的泡泡*/
    public removeItem(_item: Item, _addScore: boolean = true, _isDrop: boolean = false, _showEff: boolean = true) {
        if (_addScore) this.addTxt(_item.x, _item.y, this.getItemScore(_item.data, this.scoreStep, _isDrop));
        if (_showEff) AnimManager.show(this.mItemParent, _item.x - 50, _item.y - 50, AnimManager.BombSpAnim, null, 200);
        if ((_item.data.isPao || _item.data.isKe) && !_isDrop && _showEff) {
            SoundManager.playSound("bomb.mp3")

        }
        _item.removeSelf();
        Laya.Pool.recover(ItemBaseData.ITEM_FLAG, _item);
        if (_item.data.isPao && !_item.isFire) this.distroyPaoNum++;
        if (!this.isWuJiMode) EventCenter.getIns().event(EventEunm.taskChange);
    }

    private goDiamandPos(titem: Item) {
        let tp = this.mItemParent.localToGlobal(new Laya.Point(titem.x, titem.y));
        this.mScene.addChild(titem);
        titem.setPos(tp.x, tp.y);
        Laya.Tween.to(titem, { y: titem.y + 10 }, 200, null, Laya.Handler.create(this, () => {
            Laya.Tween.to(titem, { x: 513, y: 29 }, 1000, Laya.Ease.circIn, Laya.Handler.create(this, () => {
                this.diamand++;
                this.removeItem(titem);
            }))
        }))
    }

    private changeDataToId(_item: Item, _id: number) {
        _item.changeDataToId(_id);
        let trcs = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
        let titem: Item;
        for (let i = 0; i < trcs.length; i++) {
            titem = this.grid.getItem(trcs[i].row, trcs[i].col);
            if (titem && titem.data.isPao && titem.data.id != _id) titem.changeDataToId(_id);
        }
    }

    private mposP: Laya.Point;
    public checkItemParentPos(tw: boolean = true, offy: number = 0) {
        //if(!this.mposP)this.mposP=new Laya.Point(0,this.showRowY);
        let trow = this.grid.getMaxItemRow();
        if (this.mIsMoving || (this.mItemParent.y >= this.mItemparentY && trow <= this.showRow)) return;
        let ty: number = this.mItemparentY + trow * ItemBaseData.WIDTH;
        //let tp = this.mItemParent.globalToLocal(this.mposP);
        let tdist = this.showRowY - ty;
        if (tdist > this.mItemparentY) tdist = this.mItemparentY;
        if (Math.abs(tdist) >= ItemBaseData.WIDTH) {
            if (tw) {
                this.mIsMoving = true;
                Laya.Tween.to(this.mItemParent, { y: tdist }, 500, null, Laya.Handler.create(this, () => {
                    this.mIsMoving = false;
                }));
            } else {
                this.mItemParent.y = tdist + offy;
            }


        }
    }

    private mWuJinLimitP: Laya.Point = new Laya.Point(0, 480);
    public checkRowAdd() {
        let trow = this.grid.getMaxItemRow();
        if (this.mIsMoving || this.mItemParent.y >= this.mItemparentY) return;
        let ty: number = this.mItemparentY + trow * ItemBaseData.WIDTH;
        let tdist = 250 - ty;
        if (tdist > this.mItemparentY) tdist = this.mItemparentY;
        if (tdist > this.mItemParent.y) {
            this.mIsMoving = true;
            Laya.Tween.to(this.mItemParent, { y: tdist }, 500, null, Laya.Handler.create(this, () => {
                this.mIsMoving = false;
            }));
        }

    }

    public downRow(dist: number = 50) {
        if (this.mIsMoving || this.mItemParent.y >= this.mItemparentY) return;
        let ty = this.mItemParent.y + dist;
        if (ty > this.mItemparentY) ty = this.mItemparentY;
        this.mIsMoving = true;
        Laya.Tween.to(this.mItemParent, { y: ty }, 500, null, Laya.Handler.create(this, () => {
            this.mIsMoving = false;
        }));

    }

    private wuJinAdd(row: number, col: number) {
        let titem = this.createWuJinRandomItem();
        titem.setRC(row, col);
        return this.mGrid.addItem(titem);
    }

    public lvup() {
        this.mHardLv++;
    }

    /**根据难度每次产生多少个*/
    public getHardLvObsNum() {
        let tnum = 3;
        if(this.mHardLv>5)tnum=6;
        else if(this.mHardLv>3)tnum=5;
        else if(this.mHardLv>1)tnum=4;
        return tnum;
    }

    public getHardRow() {
        let trow = 300;
        if (this.mHardLv >= 1) trow = 300;
        else if (this.mHardLv >= 2) trow = 400;
        else if (this.mHardLv >= 3) trow = 500;
        else if (this.mHardLv >= 4) trow = 600;
        //trow = 40;
        return trow;
    }

    /**获得泡泡的id*/
    public getPaoId(index: number) {
        if (index < 0) {
            console.log("getPaoId err", index);
        }
        let tpaoid = PlayerData.getIns().currItemType * 5 + index;
        return tpaoid;
    }

    public createWuJinRandomItem() {
        let tlen: number = 4;
        let tcreatePer = 0.7;
        if (this.mHardLv >= 1) {
            
            tcreatePer = 0.5;
        } else if (this.mHardLv >= 2) {
            
            tcreatePer = 0.3;
        } else if (this.mHardLv >= 3) {
            
            tcreatePer = 0.2;
        }
        let tindex = 1 + Math.floor(Math.random() * tlen);
        let tpaoid = this.getPaoId(tindex);
        let tawardid: number = 0;
        let tdata: ItemBaseData = new ItemBaseData();
        if (Math.random() < tcreatePer) {
            tawardid = this.mAwardItemIds[Math.floor(Math.random() * this.mAwardItemIds.length)];
        }
        if (tawardid) tdata.copy(GameCfgData.getIns().getItem(tawardid));
        else {
            tdata.copy(GameCfgData.getIns().getItem(tpaoid));
        }
        let titm: Item = this.grid.getItemInPool();
        titm.setData(tdata)
        return titm;
    }

    public getConnectSpItem(_item: Item) {
        let tarr: Array<Item> = [];
        let tSparr: Array<Item> = [];
        let tbrcs = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
        let titem: Item;
        let tcache = {};
        for (let i = 0; i < tbrcs.length; i++) {
            titem = this.grid.getItem(tbrcs[i].row, tbrcs[i].col);
            if (titem && titem.data.isSpPao) {
                tSparr.push(titem);
            }
            tcache[tbrcs[i].row + "_" + tbrcs[i].col] = 1;
        }
       
        return { arr: tarr, spArr: tSparr };
    }

    public getSpItemDisposeItems(_item: Item, _cache) {
        let tarr: Array<Item> = [];
        let tSparr: Array<Item> = [];
        return { arr: tarr, spArr: tSparr };
    }

    public getBombPos(_row: number, _col: number) {
        let tarr = [];
        return tarr;
    }

    public dropAll() {
        let tmaxRow = this.mGrid.getMaxItemRow();
        let titem: Item;
        for (let row = 0; row <= tmaxRow; row++) {
            for (let col = 0; col < this.mGrid.col; col++) {
                titem = this.mGrid.getItem(row, col);
                if (titem) {
                    this.mGrid.removeItem(titem, false);
                    this.dropItem(titem);
                }
            }
        }
    }

    public getTaskStar() {
        return this.gateData.getTaskStar(this.score);
    }
    public getTaskPross() {
        let tper = this.distroyPaoNum / this.maxPao;
        if (this.gateData.task == 1) {
            tper = this.diamand / this.maxDiamand;
        } else if (this.gateData.task == 2) {
            tper = this.diamand / this.maxDiamand2;
        }
        return tper;
    }

    public getTaskProssStr() {
        let tstr = this.distroyPaoNum + "/" + this.maxPao;
        if (this.gateData.task == 1) {
            tstr = this.diamand + "/" + this.maxDiamand;
        } else if (this.gateData.task == 2) {
            tstr = this.diamand + "/" + this.maxDiamand2;
        }
        return tstr;
    }

    private mOverP: Laya.Point = new Laya.Point(0, -850);
    public isWuJinOver() {
        let tmaxrow = this.mGrid.getMaxItemRow();
        let ty: number = this.mItemparentY + tmaxrow * ItemBaseData.WIDTH;
        let tdist = -ty - this.mItemParent.y;
        //console.log("isWuJinOver", tdist, this.mItemParent.y);
        if (tdist < this.mOverP.y) return true;
        return false;

    }

    public clear() {
        this.grid.clear();
    }

    public get row(): number {
        return this.mGrid.row;
    }

    public get col(): number {
        return this.mGrid.col;
    }

    public get grid(): Grid {
        return this.mGrid;
    }

    public get gateData(): GateData {
        return this.mGateData;
    }

    private getDelayTime(_isEff: boolean) {
        let ttimes = _isEff ? this.mSpRemoveIndex - 1 : this.mSpRemoveIndex;
        let time = ttimes > 0 ? ttimes * 500 : 0;
        return time;
    }


    private findAndRemoveRandomItems(_item: Item, _cache, _len: number = 3) {
        let tarr = [];
        let tselectArr = this.find3RandomItem(_cache, _len);
        if (tselectArr.length) {
            this.addTEff(_item.x, _item.y, tselectArr);
            for (let i = 0; i < tselectArr.length; i++) {
                if (tselectArr[i].data.isSpPao) {
                    tarr.push(tselectArr[i]);
                } else {
                    this.mGrid.removeItem(tselectArr[i], false);
                    Laya.timer.once(this.getDelayTime(false), this, (_item) => {
                        this.removeItem(_item);
                    }, [tselectArr[i]])
                }
            }
        }
        return tarr;
    }

    public find3RandomItem(_cache, _len: number = 3) {
        let tmaxRow = this.mGrid.getMaxItemRow();
        let tminRow = tmaxRow - 9;
        if (tminRow < 0) tminRow = 0;
        let titem: Item;
        let tselectArr = [];
        let tfindarr = [];
        for (let row = tmaxRow; row >= tminRow; row--) {
            for (let col = 0; col < this.mGrid.col; col++) {
                titem = this.mGrid.getItem(row, col);
                if (titem && !_cache[titem.data.rcFlag] && titem.data.isPao) {
                    tfindarr.push(titem);
                }
            }
        }
        if (tfindarr.length) {
            if (tfindarr.length <= _len) {
                tselectArr = tfindarr;
            } else {
                for (let i = 0; i < _len; i++) {
                    tselectArr.push(tfindarr.splice(Math.floor(Math.random() * tfindarr.length), 1)[0]);
                }
            }
        }
        return tselectArr;
    }

    private findAndRemoveBombItems(_item: Item, _cache, _len: number = 1) {
        let tarr = [];
        let trcs = ItemBaseData.getBoudsRcs(_item.data.row, _item.data.col);
        let titem: Item;
        let time = this.getDelayTime(false);
        for (let i = 0; i < trcs.length; i++) {
            titem = this.mGrid.getItem(trcs[i].row, trcs[i].col);
            if (titem) {

                if (titem.data.isSpPao) {
                    tarr.push(titem);
                } else if (titem.data.isCanConnectDisposePao || titem.data.isCanBombDosposeSpItem) {
                    this.mGrid.removeItem(titem, false)
                    Laya.timer.once(time, this, (_item: Item) => {
                        this.removeItem(_item, true, false, false);
                    }, [titem]);
                }
            }

        }
        return tarr;
    }

    private findLineItems(_item: Item, _cache) {
        let tarr = [];
        let titem: Item;
        for (let col = 0; col < this.grid.col; col++) {
            titem = this.mGrid.getItem(_item.data.row, col);
            if (titem && titem != _item && _item.data.isCanDispose) {
                tarr.push(titem);
            }
        }
        return tarr;
    }

    private getItemScore(_data: ItemBaseData, _step: number = 0, _isDrop: boolean = false) {
        let tscsore = 20 + _step * this.stepScore;
        if (_isDrop) {
            tscsore = _data.getDropScore();
        } 
        return tscsore;
    }

    public get isMoving() {
        return this.mIsMoving;
    }

    public get isEmpty() {
        for (let col = 0; col < this.mGrid.col; col++) {
            if (this.mGrid.getItem(0, col)) return false;
        }
        return true;
    }

    public static getIns(): GridManager {
        if (!this._ins) this._ins = new GridManager();
        return this._ins;
    }
}